﻿using System;
using System.Data;
using System.Configuration;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Reflection;
using System.ComponentModel;

namespace PickGold.Generic
{
	/// <summary>
	/// 递归枚举类
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class Recursion<T> : IEnumerable<T>, IEnumerator<T>
	{
		#region inner class

		class IoItem : IEnumerable<T>, IDisposable
		{
			public IoItem(IEnumerable<T> enumerable)
			{
				this.Enumerable = enumerable;
			}

			public IoItem(Func<T, IEnumerable<T>> lister, T item)
			{
				this.Enumerable = lister(item);
			}

			public IEnumerable<T> Enumerable;
			private IEnumerator<T> _Enumerator;

			public IEnumerator<T> GetEnumerator()
			{
				if (this._Enumerator == null)
					this._Enumerator = this.Enumerable.GetEnumerator();
				return this._Enumerator;
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				return this.GetEnumerator();
			}

			public void Dispose()
			{
				if (this._Enumerator != null)
					this._Enumerator.Dispose();
				this._Enumerator = null;
				this.Enumerable = null;
			}
		}

		interface Io : IDisposable
		{
			int Count { get; }

			void I(IoItem item);
			IoItem O();
			IoItem P();
		}

		class StackIo : Io
		{
			private Stack<IoItem> _C;

			public StackIo()
			{
				this._C = new Stack<IoItem>();
			}

			public int Count
			{
				get
				{
					return this._C.Count;
				}
			}

			public void I(IoItem item)
			{
				this._C.Push(item);
			}

			public IoItem O()
			{
				return this._C.Pop();
			}

			public IoItem P()
			{
				return this._C.Peek();
			}

			public void Dispose()
			{
				if (this._C == null)
					return;

				while (this._C.Count > 0)
					this._C.Pop().Dispose();
				//this._C.Clear();
				this._C = null;
			}
		}

		class QueueIo : Io
		{
			private Queue<IoItem> _C;

			public QueueIo()
			{
				this._C = new Queue<IoItem>();
			}

			public int Count
			{
				get
				{
					return this._C.Count;
				}
			}

			public void I(IoItem item)
			{
				this._C.Enqueue(item);
			}

			public IoItem O()
			{
				return this._C.Dequeue();
			}

			public IoItem P()
			{
				return this._C.Peek();
			}

			public void Dispose()
			{
				if (this._C == null)
					return;

				while (this._C.Count > 0)
					this._C.Dequeue().Dispose();
				//this._C.Clear();
				this._C = null;
			}
		}

		#endregion

		private Func<T, IEnumerable<T>> _Lister;
		private Io _IO;
		private T _Current;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="lister"></param>
		/// <param name="df"></param>
		public Recursion(Func<T, IEnumerable<T>> lister, bool df)
		{
			this._Lister = lister;
			if (df)
				this._IO = new StackIo();
			else
				this._IO = new QueueIo();
			//this.Reset();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public IEnumerator<T> GetEnumerator()
		{
			if (this._IO == null)
				throw new ObjectDisposedException(this.GetType().Name);

			//return this;
			var e = new Recursion<T>(this._Lister, this._IO is StackIo);
			e.Reset();
			return e;
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		/// <summary>
		/// 
		/// </summary>
		public T Current
		{
			get
			{
				if (this._IO == null)
					throw new ObjectDisposedException(this.GetType().Name);

				//return this._Stack.Peek().Current;
				return this._Current;
			}
		}

		object IEnumerator.Current
		{
			get
			{
				return this.Current;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool MoveNext()
		{
			if (this._IO == null)
				throw new ObjectDisposedException(this.GetType().Name);

			while (this._IO.Count > 0)
			{
				var e = this._IO.P().GetEnumerator();
				while (e.MoveNext())
				{
					var v = this._Lister(e.Current);
					if (v != null)
					{
						this._IO.I(new IoItem(v));
						this._Current = e.Current;
						return true;
					}
				}

				this._IO.O();
				e.Dispose();
				this._Current = default(T);
			}

			return false;
		}

		/// <summary>
		/// 
		/// </summary>
		public void Reset()
		{
			if (this._IO == null)
				throw new ObjectDisposedException(this.GetType().Name);

			while (this._IO.Count > 0)
				this._IO.O().Dispose();
			this._Current = default(T);
			this._IO.I(new IoItem(this._Lister, this._Current));
		}

		/// <summary>
		/// 
		/// </summary>
		public void Dispose()
		{
			this._Lister = null;
			if (this._IO != null)
				this._IO.Dispose();
			this._IO = null;
			this._Current = default(T);
		}
	}
}
